home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / nt / emacssrc.zip / EMACSSRC.TAR / emacs-19.17 / lisp / forms.el < prev    next >
Text File  |  1993-07-23  |  52KB  |  1,626 lines

  1. ;;; forms.el -- Forms mode: edit a file as a form to fill in.
  2. ;;; Copyright (C) 1991, 1993 Free Software Foundation, Inc.
  3.  
  4. ;; Author: Johan Vromans <jv@mh.nl>
  5. ;; Version: 2.0
  6.  
  7. ;; This file is part of GNU Emacs.
  8.  
  9. ;; GNU Emacs is free software; you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation; either version 2, or (at your option)
  12. ;; any later version.
  13.  
  14. ;; GNU Emacs is distributed in the hope that it will be useful,
  15. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. ;; GNU General Public License for more details.
  18.  
  19. ;; You should have received a copy of the GNU General Public License
  20. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  21. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  
  23. ;;; Commentary:
  24.  
  25. ;;; Visit a file using a form.
  26. ;;;
  27. ;;; === Naming conventions
  28. ;;;
  29. ;;; The names of all variables and functions start with 'form-'.
  30. ;;; Names which start with 'form--' are intended for internal use, and
  31. ;;; should *NOT* be used from the outside.
  32. ;;;
  33. ;;; All variables are buffer-local, to enable multiple forms visits 
  34. ;;; simultaneously.
  35. ;;; Variable `forms--mode-setup' is local to *ALL* buffers, for it 
  36. ;;; controls if forms-mode has been enabled in a buffer.
  37. ;;;
  38. ;;; === How it works ===
  39. ;;;
  40. ;;; Forms mode means visiting a data file which is supposed to consist
  41. ;;; of records each containing a number of fields.  The records are
  42. ;;; separated by a newline, the fields are separated by a user-defined
  43. ;;; field separater (default: TAB).
  44. ;;; When shown, a record is transferred to an Emacs buffer and
  45. ;;; presented using a user-defined form.  One record is shown at a
  46. ;;; time.
  47. ;;;
  48. ;;; Forms mode is a composite mode.  It involves two files, and two
  49. ;;; buffers.
  50. ;;; The first file, called the control file, defines the name of the
  51. ;;; data file and the forms format.  This file buffer will be used to
  52. ;;; present the forms.
  53. ;;; The second file holds the actual data.  The buffer of this file
  54. ;;; will be buried, for it is never accessed directly.
  55. ;;;
  56. ;;; Forms mode is invoked using M-x forms-find-file control-file .
  57. ;;; Alternativily `forms-find-file-other-window' can be used.
  58. ;;;
  59. ;;; You may also visit the control file, and switch to forms mode by hand
  60. ;;; with M-x forms-mode .
  61. ;;;
  62. ;;; Automatic mode switching is supported if you specify 
  63. ;;; "-*- forms -*-" in the first line of the control file.
  64. ;;; 
  65. ;;; The control file is visited, evaluated using `eval-current-buffer',
  66. ;;; and should set at least the following variables:
  67. ;;;
  68. ;;;    forms-file                [string]
  69. ;;;            The name of the data file.
  70. ;;;
  71. ;;;    forms-number-of-fields            [integer]
  72. ;;;            The number of fields in each record.
  73. ;;;
  74. ;;;    forms-format-list            [list]
  75. ;;;            Formatting instructions.
  76. ;;;
  77. ;;; `forms-format-list' should be a list, each element containing
  78. ;;;
  79. ;;;   - a string, e.g. "hello".  The string is inserted in the forms
  80. ;;;    "as is".
  81. ;;;   
  82. ;;;   - an integer, denoting a field number.
  83. ;;;    The contents of this field are inserted at this point.
  84. ;;;     Fields are numbered starting with number one.
  85. ;;;   
  86. ;;;   - a function call, e.g. (insert "text").
  87. ;;;    This function call is dynamically evaluated and should return a
  88. ;;;     string.  It should *NOT* have side-effects on the forms being
  89. ;;;     constructed.  The current fields are available to the function
  90. ;;;     in the variable `forms-fields', they should *NOT* be modified.
  91. ;;;   
  92. ;;;   - a lisp symbol, that must evaluate to one of the above.
  93. ;;;
  94. ;;; Optional variables which may be set in the control file:
  95. ;;;
  96. ;;;    forms-field-sep                [string, default TAB]
  97. ;;;            The field separator used to separate the
  98. ;;;            fields in the data file.  It may be a string.
  99. ;;;
  100. ;;;    forms-read-only                [bool, default nil]
  101. ;;;            Non-nil means that the data file is visited
  102. ;;;            read-only (view mode) as opposed to edit mode.
  103. ;;;            If no write access to the data file is
  104. ;;;            possible, view mode is enforced. 
  105. ;;;
  106. ;;;    forms-multi-line            [string, default "^K"]
  107. ;;;            If non-null the records of the data file may
  108. ;;;            contain fields that can span multiple lines in
  109. ;;;            the form.
  110. ;;;            This variable denotes the separator character
  111. ;;;            to be used for this purpose.  Upon display, all
  112. ;;;            occurrencies of this character are translated
  113. ;;;            to newlines.  Upon storage they are translated
  114. ;;;            back to the separator character.
  115. ;;;
  116. ;;;    forms-forms-scroll            [bool, default t]
  117. ;;;            Non-nil means: rebind locally the commands that
  118. ;;;            perform `scroll-up' or `scroll-down' to use
  119. ;;;            `forms-next-field' resp. `forms-prev-field'.
  120. ;;;
  121. ;;;    forms-forms-jump            [bool, default t]
  122. ;;;            Non-nil means: rebind locally the commands that
  123. ;;;            perform `beginning-of-buffer' or `end-of-buffer'
  124. ;;;            to perform `forms-first-field' resp. `forms-last-field'.
  125. ;;;
  126. ;;;    forms-new-record-filter            [symbol, no default]
  127. ;;;            If defined: this should be the name of a 
  128. ;;;            function that is called when a new
  129. ;;;            record is created.  It can be used to fill in
  130. ;;;            the new record with default fields, for example.
  131. ;;;            Instead of the name of the function, it may
  132. ;;;            be the function itself.
  133. ;;;
  134. ;;;    forms-modified-record-filter        [symbol, no default]
  135. ;;;            If defined: this should be the name of a 
  136. ;;;            function that is called when a record has
  137. ;;;            been modified.  It is called after the fields
  138. ;;;            are parsed.  It can be used to register
  139. ;;;            modification dates, for example.
  140. ;;;            Instead of the name of the function, it may
  141. ;;;            be the function itself.
  142. ;;;
  143. ;;;    forms-use-text-properties        [bool, see text for default]
  144. ;;;            This variable controls if forms mode should use
  145. ;;;            text properties to protect the form text from being
  146. ;;;            modified (using text-property `read-only').
  147. ;;;            Also, the read-write fields are shown using a
  148. ;;;            distinct face, if possible.
  149. ;;;            This variable defaults to t if running Emacs 19
  150. ;;;            with text properties.
  151. ;;;            The default face to show read-write fields is
  152. ;;;            copied from face `region'.
  153. ;;;
  154. ;;;    forms-ro-face                 [symbol, default 'default]
  155. ;;;            This is the face that is used to show
  156. ;;;            read-only text on the screen.If used, this
  157. ;;;            variable should be set to a symbol that is a
  158. ;;;            valid face.
  159. ;;;            E.g.
  160. ;;;              (make-face 'my-face)
  161. ;;;              (setq forms-ro-face 'my-face)
  162. ;;;
  163. ;;;    forms-rw-face                [symbol, default 'region]
  164. ;;;            This is the face that is used to show
  165. ;;;            read-write text on the screen.
  166. ;;;
  167. ;;; After evaluating the control file, its buffer is cleared and used
  168. ;;; for further processing.
  169. ;;; The data file (as designated by `forms-file') is visited in a buffer
  170. ;;; `forms--file-buffer' which will not normally be shown.
  171. ;;; Great malfunctioning may be expected if this file/buffer is modified
  172. ;;; outside of this package while it is being visited!
  173. ;;;
  174. ;;; Normal operation is to transfer one line (record) from the data file,
  175. ;;; split it into fields (into `forms--the-record-list'), and display it
  176. ;;; using the specs in `forms-format-list'.
  177. ;;; A format routine `forms--format' is built upon startup to format 
  178. ;;; the records according to `forms-format-list'.
  179. ;;;
  180. ;;; When a form is changed the record is updated as soon as this form
  181. ;;; is left.  The contents of the form are parsed using information
  182. ;;; obtained from `forms-format-list', and the fields which are
  183. ;;; deduced from the form are modified.  Fields not shown on the forms
  184. ;;; retain their origional values.  The newly formed record then
  185. ;;; replaces the contents of the old record in `forms--file-buffer'.
  186. ;;; A parse routine `forms--parser' is built upon startup to parse
  187. ;;; the records.
  188. ;;;
  189. ;;; Two exit functions exist: `forms-exit' and `forms-exit-no-save'.
  190. ;;; `forms-exit' saves the data to the file, if modified.
  191. ;;; `forms-exit-no-save` does not.  However, if `forms-exit-no-save'
  192. ;;; is executed and the file buffer has been modified, Emacs will ask
  193. ;;; questions anyway.
  194. ;;;
  195. ;;; Other functions provided by forms mode are:
  196. ;;;
  197. ;;;    paging (forward, backward) by record
  198. ;;;    jumping (first, last, random number)
  199. ;;;    searching
  200. ;;;    creating and deleting records
  201. ;;;    reverting the form (NOT the file buffer)
  202. ;;;    switching edit <-> view mode v.v.
  203. ;;;    jumping from field to field
  204. ;;;
  205. ;;; As an documented side-effect: jumping to the last record in the
  206. ;;; file (using forms-last-record) will adjust forms--total-records if
  207. ;;; needed.
  208. ;;;
  209. ;;; Commands and keymaps:
  210. ;;;
  211. ;;; A local keymap `forms-mode-map' is used in the forms buffer.
  212. ;;; If the forms is in view mode, this keymap is used so all forms mode
  213. ;;; functions are accessible.
  214. ;;; If the forms is in edit mode, this map can be accessed with C-c prefix.
  215. ;;;
  216. ;;; Default bindings:
  217. ;;;
  218. ;;;    \C-c    forms-mode-map
  219. ;;;    TAB    forms-next-field
  220. ;;;    SPC     forms-next-record
  221. ;;;    <    forms-first-record
  222. ;;;    >    forms-last-record
  223. ;;;    ?    describe-mode
  224. ;;;    d    forms-delete-record
  225. ;;;    e    forms-edit-mode
  226. ;;;    i    forms-insert-record
  227. ;;;    j    forms-jump-record
  228. ;;;    n    forms-next-record
  229. ;;;    p    forms-prev-record
  230. ;;;    q    forms-exit
  231. ;;;    s    forms-search
  232. ;;;    v    forms-view-mode
  233. ;;;    x    forms-exit-no-save
  234. ;;;    DEL    forms-prev-record
  235. ;;;
  236. ;;; For convenience, TAB is always bound to `forms-next-field', so you
  237. ;;; don't need the C-c prefix for this command.
  238. ;;;
  239. ;;; As mentioned above (see `forms-forms-scroll' and `forms-forms-jump')
  240. ;;; the bindings of standard functions `scroll-up', `scroll-down',
  241. ;;; `beginning-of-buffer' and `end-of-buffer' are locally replaced with
  242. ;;; forms mode functions next/prev record and first/last
  243. ;;; record.
  244. ;;;
  245. ;;; `local-write-file hook' is defined to save the actual data file
  246. ;;; instead of the buffer data, `revert-file-hook' is defined to
  247. ;;; revert a forms to original.
  248.  
  249. ;;; Code:
  250.  
  251. ;;; Global variables and constants:
  252.  
  253. (provide 'forms)            ;;; official
  254. (provide 'forms-mode)            ;;; for compatibility
  255.  
  256. (defconst forms-version "2.0"
  257.   "Version of forms-mode implementation.")
  258.  
  259. (defvar forms-mode-hooks nil
  260.   "Hook functions to be run upon entering Forms mode.")
  261.  
  262. ;;; Mandatory variables - must be set by evaluating the control file.
  263.  
  264. (defvar forms-file nil
  265.   "Name of the file holding the data.")
  266.  
  267. (defvar forms-format-list nil
  268.   "List of formatting specifications.")
  269.  
  270. (defvar forms-number-of-fields nil
  271.   "Number of fields per record.")
  272.  
  273. ;;; Optional variables with default values.
  274.  
  275. (defvar forms-field-sep "\t"
  276.   "Field separator character (default TAB).")
  277.  
  278. (defvar forms-read-only nil
  279.   "Non-nil means: visit the file in view (read-only) mode.
  280. (Defaults to the write access on the data file).")
  281.  
  282. (defvar forms-multi-line "\C-k"
  283.   "If not nil: use this character to separate multi-line fields (default C-k).")
  284.  
  285. (defvar forms-forms-scroll t
  286.   "*Non-nil means replace scroll-up/down commands in Forms mode.
  287. The replacement commands performs forms-next/prev-record.")
  288.  
  289. (defvar forms-forms-jump t
  290.   "*Non-nil means redefine beginning/end-of-buffer in Forms mode.
  291. The replacement commands performs forms-first/last-record.")
  292.  
  293. (defvar forms-new-record-filter nil
  294.   "The name of a function that is called when a new record is created.")
  295.  
  296. (defvar forms-modified-record-filter nil
  297.   "The name of a function that is called when a record has been modified.")
  298.  
  299. (defvar forms-fields nil
  300.   "List with fields of the current forms.  First field has number 1.
  301. This variable is for use by the filter routines only. 
  302. The contents may NOT be modified.")
  303.  
  304. (defvar forms-use-text-properties (fboundp 'set-text-properties)
  305.   "*Non-nil means: use emacs-19 text properties.
  306. Defaults to t if this emacs is capable of handling text properties.")
  307.  
  308. (defvar forms-ro-face 'default
  309.   "The face (a symbol) that is used to display read-only text on the screen.")
  310.  
  311. (defvar forms-rw-face 'region
  312.   "The face (a symbol) that is used to display read-write text on the screen.")
  313.  
  314. ;;; Internal variables.
  315.  
  316. (defvar forms--file-buffer nil
  317.   "Buffer which holds the file data")
  318.  
  319. (defvar forms--total-records 0
  320.   "Total number of records in the data file.")
  321.  
  322. (defvar forms--current-record 0
  323.   "Number of the record currently on the screen.")
  324.  
  325. (defvar forms-mode-map nil        ; yes - this one is global
  326.    "Keymap for form buffer.")
  327.  
  328. (defvar forms--markers nil
  329.   "Field markers in the screen.")
  330.  
  331. (defvar forms--dyntexts nil
  332.   "Dynamic texts (resulting from function calls) on the screen.")
  333.  
  334. (defvar forms--the-record-list nil 
  335.    "List of strings of the current record, as parsed from the file.")
  336.  
  337. (defvar forms--search-regexp nil
  338.   "Last regexp used by forms-search.")
  339.  
  340. (defvar forms--format nil
  341.   "Formatting routine.")
  342.  
  343. (defvar forms--parser nil
  344.   "Forms parser routine.")
  345.  
  346. (defvar forms--mode-setup nil
  347.   "To keep track of forms-mode being set-up.")
  348. (make-variable-buffer-local 'forms--mode-setup)
  349.  
  350. (defvar forms--new-record-filter nil
  351.   "Set if a new record filter has been defined.")
  352.  
  353. (defvar forms--modified-record-filter nil
  354.   "Set if a modified record filter has been defined.")
  355.  
  356. (defvar forms--dynamic-text nil
  357.   "Array that holds dynamic texts to insert between fields.")
  358.  
  359. (defvar forms--elements nil
  360.   "Array with the order in which the fields are displayed.")
  361.  
  362. (defvar forms--ro-face nil
  363.   "Face used to represent read-only data on the screen.")
  364.  
  365. (defvar forms--rw-face nil
  366.   "Face used to represent read-write data on the screen.")
  367.  
  368. ;;;###autoload 
  369. (defun forms-mode (&optional primary)
  370.   "Major mode to visit files in a field-structured manner using a form.
  371.  
  372. Commands (prefix with C-c if not in read-only mode):
  373. \\{forms-mode-map}"
  374.  
  375.   (interactive)                ; no - 'primary' is not prefix arg
  376.  
  377.   ;; This is not a simple major mode, as usual.  Therefore, forms-mode
  378.   ;; takes an optional argument `primary' which is used for the
  379.   ;; initial set-up.  Normal use would leave `primary' to nil.
  380.   ;; A global buffer-local variable `forms--mode-setup' has the same
  381.   ;; effect but makes it possible to auto-invoke forms-mode using
  382.   ;; `find-file'.
  383.   ;; Note: although it seems logical to have `make-local-variable'
  384.   ;; executed where the variable is first needed, I have deliberately
  385.   ;; placed all calls in this function.
  386.  
  387.   ;; Primary set-up: evaluate buffer and check if the mandatory
  388.   ;; variables have been set.
  389.   (if (or primary (not forms--mode-setup))
  390.       (progn
  391.     ;;(message "forms: setting up...")
  392.     (kill-all-local-variables)
  393.  
  394.     ;; Make mandatory variables.
  395.     (make-local-variable 'forms-file)
  396.     (make-local-variable 'forms-number-of-fields)
  397.     (make-local-variable 'forms-format-list)
  398.  
  399.     ;; Make optional variables.
  400.     (make-local-variable 'forms-field-sep)
  401.         (make-local-variable 'forms-read-only)
  402.         (make-local-variable 'forms-multi-line)
  403.     (make-local-variable 'forms-forms-scroll)
  404.     (make-local-variable 'forms-forms-jump)
  405.     (make-local-variable 'forms-use-text-properties)
  406.     (make-local-variable 'forms--new-record-filter)
  407.     (make-local-variable 'forms--modified-record-filter)
  408.  
  409.     ;; Make sure no filters exist.
  410.     (fmakunbound 'forms-new-record-filter)
  411.     (fmakunbound 'forms-modified-record-filter)
  412.  
  413.     ;; If running Emacs 19 under X, setup faces to show read-only and 
  414.     ;; read-write fields.
  415.     (if (fboundp 'make-face)
  416.         (progn
  417.           (make-local-variable 'forms-ro-face)
  418.           (make-local-variable 'forms-rw-face)))
  419.  
  420.     ;; eval the buffer, should set variables
  421.     ;;(message "forms: processing control file...")
  422.     (eval-current-buffer)
  423.  
  424.     ;; check if the mandatory variables make sense.
  425.     (or forms-file
  426.         (error "'forms-file' has not been set"))
  427.     (or forms-number-of-fields
  428.         (error "'forms-number-of-fields' has not been set"))
  429.     (or (> forms-number-of-fields 0)
  430.         (error "'forms-number-of-fields' must be > 0")
  431.     (or (stringp forms-field-sep))
  432.         (error "'forms-field-sep' is not a string"))
  433.     (if forms-multi-line
  434.         (if (and (stringp forms-multi-line)
  435.              (eq (length forms-multi-line) 1))
  436.         (if (string= forms-multi-line forms-field-sep)
  437.             (error "'forms-multi-line' is equal to 'forms-field-sep'"))
  438.           (error "'forms-multi-line' must be nil or a one-character string")))
  439.     (or (fboundp 'set-text-properties)
  440.         (setq forms-use-text-properties nil))
  441.         
  442.     ;; Validate and process forms-format-list.
  443.     ;;(message "forms: pre-processing format list...")
  444.     (forms--process-format-list)
  445.  
  446.     ;; Build the formatter and parser.
  447.     ;;(message "forms: building formatter...")
  448.     (make-local-variable 'forms--format)
  449.     (make-local-variable 'forms--markers)
  450.     (make-local-variable 'forms--dyntexts)
  451.     (make-local-variable 'forms--elements)
  452.     ;;(message "forms: building parser...")
  453.     (forms--make-format)
  454.     (make-local-variable 'forms--parser)
  455.     (forms--make-parser)
  456.     ;;(message "forms: building parser... done.")
  457.  
  458.     ;; Check if record filters are defined.
  459.     (setq forms--new-record-filter 
  460.           (cond
  461.            ((fboundp 'forms-new-record-filter)
  462.         (symbol-function 'forms-new-record-filter))
  463.            ((and (boundp 'forms-new-record-filter)
  464.              (fboundp forms-new-record-filter))
  465.         forms-new-record-filter)))
  466.     (fmakunbound 'forms-new-record-filter)
  467.     (setq forms--modified-record-filter 
  468.           (cond
  469.            ((fboundp 'forms-modified-record-filter)
  470.         (symbol-function 'forms-modified-record-filter))
  471.            ((and (boundp 'forms-modified-record-filter)
  472.              (fboundp forms-modified-record-filter))
  473.         forms-modified-record-filter)))
  474.     (fmakunbound 'forms-modified-record-filter)
  475.  
  476.     ;; The filters acces the contents of the forms using `forms-fields'.
  477.     (make-local-variable 'forms-fields)
  478.  
  479.     ;; Dynamic text support.
  480.     (make-local-variable 'forms--dynamic-text)
  481.  
  482.     ;; Prevent accidental overwrite of the control file and autosave.
  483.     (setq buffer-file-name nil)
  484.     (auto-save-mode nil)
  485.  
  486.     ;; Prepare this buffer for further processing.
  487.     (setq buffer-read-only nil)
  488.     (erase-buffer)
  489.  
  490.     ;;(message "forms: setting up... done.")
  491.     ))
  492.  
  493.   ;; Copy desired faces to the actual variables used by the forms formatter.
  494.   (if (fboundp 'make-face)
  495.       (progn
  496.     (make-local-variable 'forms--ro-face)
  497.     (make-local-variable 'forms--rw-face)
  498.     (if forms-read-only
  499.         (progn
  500.           (setq forms--ro-face forms-ro-face)
  501.           (setq forms--rw-face forms-ro-face))
  502.       (setq forms--ro-face forms-ro-face)
  503.       (setq forms--rw-face forms-rw-face))))
  504.  
  505.   ;; Make more local variables.
  506.   (make-local-variable 'forms--file-buffer)
  507.   (make-local-variable 'forms--total-records)
  508.   (make-local-variable 'forms--current-record)
  509.   (make-local-variable 'forms--the-record-list)
  510.   (make-local-variable 'forms--search-regexp)
  511.  
  512.   ;; A bug in the current Emacs release prevents a keymap
  513.   ;; which is buffer-local from being used by 'describe-mode'.
  514.   ;; Hence we'll leave it global.
  515.   ;;(make-local-variable 'forms-mode-map)
  516.   (if forms-mode-map            ; already defined
  517.       nil
  518.     ;;(message "forms: building keymap...")
  519.     (setq forms-mode-map (make-keymap))
  520.     (forms--mode-commands forms-mode-map)
  521.     ;;(message "forms: building keymap... done.")
  522.     )
  523.  
  524.   ;; find the data file
  525.   (setq forms--file-buffer (find-file-noselect forms-file))
  526.  
  527.   ;; count the number of records, and set see if it may be modified
  528.   (let (ro)
  529.     (setq forms--total-records
  530.       (save-excursion
  531.         (prog1
  532.         (progn
  533.           ;;(message "forms: counting records...")
  534.           (set-buffer forms--file-buffer)
  535.           (bury-buffer (current-buffer))
  536.           (setq ro buffer-read-only)
  537.           (count-lines (point-min) (point-max)))
  538.           ;;(message "forms: counting records... done.")
  539.           )))
  540.     (if ro
  541.     (setq forms-read-only t)))
  542.  
  543.   ;;(message "forms: proceeding setup...")
  544.   ;; set the major mode indicator
  545.   (setq major-mode 'forms-mode)
  546.   (setq mode-name "Forms")
  547.   (make-local-variable 'minor-mode-alist) ; needed?
  548.   ;;(message "forms: proceeding setup (minor mode)...")
  549.   (forms--set-minor-mode)
  550.   ;;(message "forms: proceeding setup (keymaps)...")
  551.   (forms--set-keymaps)
  552.   (make-local-variable 'local-write-file-hooks)
  553.   ;;(message "forms: proceeding setup (commands)...")
  554.   (forms--change-commands)
  555.  
  556.   ;;(message "forms: proceeding setup (buffer)...")
  557.   (set-buffer-modified-p nil)
  558.  
  559.   ;; We have our own revert function - use it
  560.   (make-local-variable 'revert-buffer-function)
  561.   (setq revert-buffer-function 'forms-revert-buffer)
  562.  
  563.   ;; setup the first (or current) record to show
  564.   (if (< forms--current-record 1)
  565.       (setq forms--current-record 1))
  566.   (forms-jump-record forms--current-record)
  567.  
  568.   ;; user customising
  569.   ;;(message "forms: proceeding setup (user hooks)...")
  570.   (run-hooks 'forms-mode-hooks)
  571.   ;;(message "forms: setting up... done.")
  572.  
  573.   ;; be helpful
  574.   (forms--help)
  575.  
  576.   ;; initialization done
  577.   (setq forms--mode-setup t))
  578.  
  579. (defun forms--process-format-list ()
  580.   ;; Validate `forms-format-list' and set some global variables.
  581.   ;; Symbols in the list are evaluated, and consecutive strings are
  582.   ;; concatenated.
  583.   ;; Array `forms--elements' is constructed that contains the order
  584.   ;; of the fields on the display. This array is used by 
  585.   ;; `forms--parser-using-text-properties' to extract the fields data
  586.   ;; from the form on the screen.
  587.   ;; Upon completion, `forms-format-list' is garanteed correct, so
  588.   ;; `forms--make-format' and `forms--make-parser' do not need to perform
  589.   ;; any checks.
  590.  
  591.   ;; Verify that `forms-format-list' is not nil.
  592.   (or forms-format-list
  593.       (error "'forms-format-list' has not been set"))
  594.   ;; It must be a list.
  595.   (or (listp forms-format-list)
  596.       (error "'forms-format-list' is not a list"))
  597.  
  598.   ;; Assume every field is painted once.
  599.   ;; `forms--elements' will grow if needed.
  600.   (setq forms--elements (make-vector forms-number-of-fields nil))
  601.  
  602.   (let ((the-list forms-format-list)    ; the list of format elements
  603.     (this-item 0)            ; element in list
  604.     (prev-item nil)
  605.     (field-num 0))            ; highest field number 
  606.  
  607.     (setq forms-format-list nil)    ; gonna rebuild
  608.  
  609.     (while the-list
  610.  
  611.       (let ((el (car-safe the-list))
  612.         (rem (cdr-safe the-list)))
  613.  
  614.     ;; If it is a symbol, eval it first.
  615.     (if (and (symbolp el)
  616.          (boundp el))
  617.         (setq el (eval el)))
  618.  
  619.     (cond
  620.  
  621.      ;; Try string ...
  622.      ((stringp el)
  623.       (if (stringp prev-item)    ; try to concatenate strings
  624.           (setq prev-item (concat prev-item el))
  625.         (if prev-item
  626.         (setq forms-format-list
  627.               (append forms-format-list (list prev-item) nil)))
  628.         (setq prev-item el)))
  629.  
  630.      ;; Try numeric ...
  631.      ((numberp el) 
  632.  
  633.       ;; Validate range.
  634.       (if (or (<= el 0)
  635.           (> el forms-number-of-fields))
  636.           (error
  637.            "Forms error: field number %d out of range 1..%d"
  638.            el forms-number-of-fields))
  639.  
  640.       ;; Store forms order.
  641.       (if (> field-num (length forms--elements))
  642.           (setq forms--elements (vconcat forms--elements (1- el)))
  643.         (aset forms--elements field-num (1- el)))
  644.       (setq field-num (1+ field-num))
  645.  
  646.       ;; Make sure the field is preceded by something.
  647.       (if prev-item
  648.           (setq forms-format-list
  649.             (append forms-format-list (list prev-item) nil))
  650.         (setq forms-format-list
  651.           (append forms-format-list (list "\n") nil)))
  652.       (setq prev-item el))
  653.  
  654.      ;; Try function ...
  655.      ((listp el)
  656.  
  657.       ;; Validate.
  658.       (or (fboundp (car-safe el))
  659.           (error 
  660.            "Forms error: not a function: %s"
  661.            (prin1-to-string (car-safe el))))
  662.  
  663.       ;; Shift.
  664.       (if prev-item
  665.           (setq forms-format-list
  666.             (append forms-format-list (list prev-item) nil)))
  667.       (setq prev-item el))
  668.  
  669.      ;; else
  670.      (t
  671.       (error "Forms error: invalid element %s"
  672.          (prin1-to-string el))))
  673.  
  674.     ;; Advance to next element of the list.
  675.     (setq the-list rem)))
  676.  
  677.     ;; Append last item.
  678.     (if prev-item
  679.     (progn
  680.       (setq forms-format-list
  681.         (append forms-format-list (list prev-item) nil))
  682.       ;; Append a newline if the last item is a field.
  683.       ;; This prevents pasrsing problems.
  684.       ;; Also it makes it possible to insert an empty last field.
  685.       (if (numberp prev-item)
  686.           (setq forms-format-list
  687.             (append forms-format-list (list "\n") nil))))))
  688.  
  689.   (forms--debug 'forms-format-list
  690.         'forms--elements))
  691.  
  692. ;; Special treatment for read-only segments.
  693. ;;
  694. ;; If text is inserted after a read-only segment, it inherits the
  695. ;; read-only properties.  This is not what we want.
  696. ;; The modification hook of the last character of the read-only segment
  697. ;; temporarily switches its properties to read-write, so the new
  698. ;; text gets the right properties.
  699. ;; The post-command-hook is used to restore the original properties.
  700. ;;
  701. ;; A character category `forms-electric' is used for the characters
  702. ;; that get the modification hook set.  Using a category, it is
  703. ;; possible to globally enable/disable the modification hook.  This is
  704. ;; necessary, since modifying a hook or setting text properties are
  705. ;; considered modifications and would trigger the hooks while building
  706. ;; the forms.
  707.  
  708. (defvar forms--ro-modification-start nil
  709.   "Record start of modification command.")
  710. (defvar forms--ro-properties nil
  711.   "Original properties of the character being overridden.")
  712.  
  713. (defun forms--romh (begin end)
  714.   "`modification-hook' function for forms-electric characters."
  715.  
  716.   ;; Note start location.
  717.   (or forms--ro-modification-start
  718.       (setq forms--ro-modification-start (point)))
  719.  
  720.   ;; Fetch current properties.
  721.   (setq forms--ro-properties 
  722.     (text-properties-at (1- forms--ro-modification-start)))
  723.  
  724.   ;; Disarm modification hook.
  725.   (setplist 'forms--electric nil)
  726.  
  727.   ;; Replace them.
  728.   (let ((inhibit-read-only t))
  729.     (set-text-properties 
  730.      (1- forms--ro-modification-start) forms--ro-modification-start
  731.      (list 'face forms--rw-face)))
  732.  
  733.   ;; Re-arm electric.
  734.   (setplist 'forms--electric '(modification-hooks (forms--romh)))
  735.  
  736.   ;; Enable `post-command-hook' to restore the properties.
  737.   (setq post-command-hook
  738.     (append (list 'forms--romh-post-command-hook) post-command-hook)))
  739.  
  740. (defun forms--romh-post-command-hook ()
  741.   "`post-command-hook' function for forms--electric characters."
  742.  
  743.   ;; Disable `post-command-hook'.
  744.   (setq post-command-hook
  745.     (delq 'forms--romh-post-command-hook post-command-hook))
  746.  
  747.   ;; Disarm modification hook.
  748.   (setplist 'forms--electric nil)
  749.  
  750.   ;; Restore properties.
  751.   (if forms--ro-modification-start
  752.       (let ((inhibit-read-only t))
  753.     (set-text-properties 
  754.      (1- forms--ro-modification-start) forms--ro-modification-start
  755.      forms--ro-properties)))
  756.  
  757.   ;; Re-arm electric.
  758.   (setplist 'forms--electric '(modification-hooks (forms--romh)))
  759.  
  760.   ;; Cleanup.
  761.   (setq forms--ro-modification-start nil))
  762.  
  763. (defvar forms--marker)
  764. (defvar forms--dyntext)
  765.  
  766. (defun forms--make-format ()
  767.   "Generate `forms--format' using the information in `forms-format-list'."
  768.  
  769.   ;; The real work is done using a mapcar of `forms--make-format-elt' on
  770.   ;; `forms-format-list'.
  771.   ;; This function sets up the necessary environment, and decides
  772.   ;; which function to mapcar.
  773.  
  774.   (let ((forms--marker 0)
  775.     (forms--dyntext 0))
  776.     (setq 
  777.      forms--format
  778.      (if forms-use-text-properties 
  779.      (` (lambda (arg)
  780.           (let ((inhibit-read-only t))
  781.         (setplist 'forms--electric nil)
  782.         (,@ (apply 'append
  783.                (mapcar 'forms--make-format-elt-using-text-properties
  784.                    forms-format-list))))
  785.           (setplist 'forms--electric
  786.             '(modification-hooks (forms--romh)))
  787.           (setq forms--ro-modification-start nil)))
  788.        (` (lambda (arg)
  789.         (,@ (apply 'append
  790.                (mapcar 'forms--make-format-elt forms-format-list)))))))
  791.  
  792.     ;; We have tallied the number of markers and dynamic texts,
  793.     ;; so we can allocate the arrays now.
  794.     (setq forms--markers (make-vector forms--marker nil))
  795.     (setq forms--dyntexts (make-vector forms--dyntext nil)))
  796.   (forms--debug 'forms--format))
  797.  
  798. (defun forms--make-format-elt-using-text-properties (el)
  799.   "Helper routine to generate format function."
  800.  
  801.   ;; The format routine `forms--format' will look like
  802.   ;;
  803.   ;; ;; preamble
  804.   ;; (lambda (arg)
  805.   ;;   (let ((inhibit-read-only t))
  806.   ;;     (setplist 'forms--electric nil)
  807.   ;;
  808.   ;;     ;; a string, e.g. "text: "
  809.   ;;     (set-text-properties 
  810.   ;;      (point)
  811.   ;;      (progn (insert "text: ") (point)) 
  812.   ;;      (list 'face forms--ro-face 'read-only 1))
  813.   ;;
  814.   ;;     ;; a field, e.g. 6
  815.   ;;     (let ((here (point)))
  816.   ;;       (aset forms--markers 0 (point-marker))
  817.   ;;       (insert (elt arg 5))
  818.   ;;       (or (= (point) here)
  819.   ;;       (set-text-properties 
  820.   ;;        here (point)
  821.   ;;        (list 'face forms--rw-face)))
  822.   ;;       (if (get-text-property (1- here) 'read-only)
  823.   ;;       (put-text-property 
  824.   ;;        (1- here) here
  825.   ;;        'category 'forms--electric)))
  826.   ;;
  827.   ;;     ;; another string, e.g. "\nmore text: "
  828.   ;;     (set-text-properties
  829.   ;;      (point)
  830.   ;;      (progn (insert "\nmore text: ") (point))
  831.   ;;      (list 'face forms--ro-face
  832.   ;;        'read-only 2))
  833.   ;;
  834.   ;;     ;; a function, e.g. (tocol 40)
  835.   ;;     (set-text-properties
  836.   ;;      (point)
  837.   ;;      (progn
  838.   ;;        (insert (aset forms--dyntexts 0 (tocol 40)))
  839.   ;;        (point))
  840.   ;;      (list 'face forms--ro-face
  841.   ;;        'read-only 2))
  842.   ;;
  843.   ;;     ;; wrap up
  844.   ;;     (setplist 'forms--electric
  845.   ;;           '(modification-hooks (forms--romh)))
  846.   ;;     (setq forms--ro-modification-start nil)
  847.   ;;     ))
  848.  
  849.   (cond
  850.    ((stringp el)
  851.     
  852.     (` ((set-text-properties 
  853.      (point)            ; start at point
  854.      (progn                ; until after insertion
  855.        (insert (, el))
  856.        (point))
  857.      (list 'face forms--ro-face    ; read-only appearance
  858.            'read-only (,@ (list (1+ forms--marker))))))))
  859.    ((numberp el)
  860.     (` ((let ((here (point)))
  861.       (aset forms--markers 
  862.         (, (prog1 forms--marker
  863.              (setq forms--marker (1+ forms--marker))))
  864.         (point-marker))
  865.       (insert (elt arg (, (1- el))))
  866.       (or (= (point) here)
  867.           (set-text-properties 
  868.            here (point)
  869.            (list 'face forms--rw-face)))
  870.       (if (get-text-property (1- here) 'read-only)
  871.           (put-text-property
  872.            (1- here) here
  873.            'category 'forms--electric))))))
  874.  
  875.    ((listp el)
  876.     (` ((set-text-properties
  877.      (point)
  878.      (progn
  879.        (insert (aset forms--dyntexts 
  880.              (, (prog1 forms--dyntext
  881.                   (setq forms--dyntext (1+ forms--dyntext))))
  882.              (, el)))
  883.        (point))
  884.      (list 'face forms--ro-face
  885.            'read-only 
  886.            (,@ (list (1+ forms--marker))))))))
  887.  
  888.    ;; end of cond
  889.    ))
  890.  
  891. (defun forms--make-format-elt (el)
  892.   "Helper routine to generate format function."
  893.  
  894.   ;; If we're not using text properties, the format routine
  895.   ;; `forms--format' will look like
  896.   ;;
  897.   ;; (lambda (arg)
  898.   ;;   ;; a string, e.g. "text: "
  899.   ;;   (insert "text: ")
  900.   ;;   ;; a field, e.g. 6
  901.   ;;   (aset forms--markers 0 (point-marker))
  902.   ;;   (insert (elt arg 5))
  903.   ;;   ;; another string, e.g. "\nmore text: "
  904.   ;;   (insert "\nmore text: ")
  905.   ;;   ;; a function, e.g. (tocol 40)
  906.   ;;   (insert (aset forms--dyntexts 0 (tocol 40)))
  907.   ;;   ... )
  908.  
  909.   (cond 
  910.    ((stringp el)
  911.     (` ((insert (, el)))))
  912.    ((numberp el)
  913.     (prog1
  914.     (` ((aset forms--markers (, forms--marker) (point-marker))
  915.         (insert (elt arg (, (1- el))))))
  916.       (setq forms--marker (1+ forms--marker))))
  917.    ((listp el)
  918.     (prog1
  919.     (` ((insert (aset forms--dyntexts (, forms--dyntext) (, el)))))
  920.       (setq forms--dyntext (1+ forms--dyntext))))))
  921.  
  922. (defvar forms--field)
  923. (defvar forms--recordv)
  924. (defvar forms--seen-text)
  925.  
  926. (defun forms--make-parser ()
  927.   "Generate `forms--parser' from the information in `forms-format-list'."
  928.  
  929.   ;; If we can use text properties, we simply set it to
  930.   ;; `forms--parser-using-text-properties'.
  931.   ;; Otherwise, the function is constructed using a mapcar of
  932.   ;; `forms--make-parser-elt on `forms-format-list'.
  933.  
  934.   (setq
  935.    forms--parser
  936.    (if forms-use-text-properties
  937.        (function forms--parser-using-text-properties)
  938.      (let ((forms--field nil)
  939.        (forms--seen-text nil)
  940.        (forms--dyntext 0))
  941.  
  942.        ;; Note: we add a nil element to the list passed to `mapcar',
  943.        ;; see `forms--make-parser-elt' for details.
  944.        (` (lambda nil
  945.         (let (here)
  946.           (goto-char (point-min))
  947.           (,@ (apply 'append
  948.              (mapcar 
  949.               'forms--make-parser-elt 
  950.               (append forms-format-list (list nil)))))))))))
  951.  
  952.   (forms--debug 'forms--parser))
  953.  
  954. (defun forms--parser-using-text-properties ()
  955.   "Extract field info from forms when using text properties."
  956.  
  957.   ;; Using text properties, we can simply jump to the markers, and
  958.   ;; extract the information up to the following read-only segment.
  959.  
  960.   (let ((i 0)
  961.     here there)
  962.     (while (< i (length forms--markers))
  963.       (goto-char (setq here (aref forms--markers i)))
  964.       (if (get-text-property here 'read-only)
  965.       (aset forms--recordv (aref forms--elements i) nil)
  966.     (if (setq there 
  967.           (next-single-property-change here 'read-only))
  968.         (aset forms--recordv (aref forms--elements i)
  969.           (buffer-substring here there))
  970.       (aset forms--recordv (aref forms--elements i)
  971.         (buffer-substring here (point-max)))))
  972.       (setq i (1+ i)))))
  973.  
  974. (defun forms--make-parser-elt (el)
  975.   "Helper routine to generate forms parser function."
  976.  
  977.   ;; The parse routine will look like:
  978.   ;;
  979.   ;; (lambda nil
  980.   ;;   (let (here)
  981.   ;;     (goto-char (point-min))
  982.   ;; 
  983.   ;;     ;;  "text: "
  984.   ;;     (if (not (looking-at "text: "))
  985.   ;;         (error "Parse error: cannot find \"text: \""))
  986.   ;;     (forward-char 6)    ; past "text: "
  987.   ;; 
  988.   ;;     ;;  6
  989.   ;;     ;;  "\nmore text: "
  990.   ;;     (setq here (point))
  991.   ;;     (if (not (search-forward "\nmore text: " nil t nil))
  992.   ;;         (error "Parse error: cannot find \"\\nmore text: \""))
  993.   ;;     (aset forms--recordv 5 (buffer-substring here (- (point) 12)))
  994.   ;;
  995.   ;;     ;;  (tocol 40)
  996.   ;;    (let ((forms--dyntext (car-safe forms--dynamic-text)))
  997.   ;;      (if (not (looking-at (regexp-quote forms--dyntext)))
  998.   ;;          (error "Parse error: not looking at \"%s\"" forms--dyntext))
  999.   ;;      (forward-char (length forms--dyntext))
  1000.   ;;      (setq forms--dynamic-text (cdr-safe forms--dynamic-text)))
  1001.   ;;     ... 
  1002.   ;;     ;; final flush (due to terminator sentinel, see below)
  1003.   ;;    (aset forms--recordv 7 (buffer-substring (point) (point-max)))
  1004.  
  1005.   (cond
  1006.    ((stringp el)
  1007.     (prog1
  1008.     (if forms--field
  1009.         (` ((setq here (point))
  1010.         (if (not (search-forward (, el) nil t nil))
  1011.             (error "Parse error: cannot find \"%s\"" (, el)))
  1012.         (aset forms--recordv (, (1- forms--field))
  1013.               (buffer-substring here
  1014.                     (- (point) (, (length el)))))))
  1015.       (` ((if (not (looking-at (, (regexp-quote el))))
  1016.           (error "Parse error: not looking at \"%s\"" (, el)))
  1017.           (forward-char (, (length el))))))
  1018.       (setq forms--seen-text t)
  1019.       (setq forms--field nil)))
  1020.    ((numberp el)
  1021.     (if forms--field
  1022.     (error "Cannot parse adjacent fields %d and %d"
  1023.            forms--field el)
  1024.       (setq forms--field el)
  1025.       nil))
  1026.    ((null el)
  1027.     (if forms--field
  1028.     (` ((aset forms--recordv (, (1- forms--field))
  1029.           (buffer-substring (point) (point-max)))))))
  1030.    ((listp el)
  1031.     (prog1
  1032.     (if forms--field
  1033.         (` ((let ((here (point))
  1034.               (forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
  1035.           (if (not (search-forward forms--dyntext nil t nil))
  1036.               (error "Parse error: cannot find \"%s\"" forms--dyntext))
  1037.           (aset forms--recordv (, (1- forms--field))
  1038.             (buffer-substring here
  1039.                       (- (point) (length forms--dyntext)))))))
  1040.       (` ((let ((forms--dyntext (aref forms--dyntexts (, forms--dyntext))))
  1041.         (if (not (looking-at (regexp-quote forms--dyntext)))
  1042.             (error "Parse error: not looking at \"%s\"" forms--dyntext))
  1043.         (forward-char (length forms--dyntext))))))
  1044.       (setq forms--dyntext (1+ forms--dyntext))
  1045.       (setq forms--seen-text t)
  1046.       (setq forms--field nil)))
  1047.    ))
  1048.  
  1049. (defun forms--set-minor-mode ()
  1050.   (setq minor-mode-alist
  1051.     (if forms-read-only
  1052.         " View"
  1053.       nil)))
  1054.  
  1055. (defun forms--set-keymaps ()
  1056.   "Set the keymaps used in this mode."
  1057.  
  1058.   (if forms-read-only
  1059.       (use-local-map forms-mode-map)
  1060.     (use-local-map (make-sparse-keymap))
  1061.     (define-key (current-local-map) "\C-c" forms-mode-map)
  1062.     (define-key (current-local-map) "\t"   'forms-next-field)))
  1063.  
  1064. (defun forms--mode-commands (map)
  1065.   "Fill map with all Forms mode commands."
  1066.  
  1067.   (define-key map "\t" 'forms-next-field)
  1068.   (define-key map " " 'forms-next-record)
  1069.   (define-key map "d" 'forms-delete-record)
  1070.   (define-key map "e" 'forms-edit-mode)
  1071.   (define-key map "i" 'forms-insert-record)
  1072.   (define-key map "j" 'forms-jump-record)
  1073.   (define-key map "n" 'forms-next-record)
  1074.   (define-key map "p" 'forms-prev-record)
  1075.   (define-key map "q" 'forms-exit)
  1076.   (define-key map "s" 'forms-search)
  1077.   (define-key map "v" 'forms-view-mode)
  1078.   (define-key map "x" 'forms-exit-no-save)
  1079.   (define-key map "<" 'forms-first-record)
  1080.   (define-key map ">" 'forms-last-record)
  1081.   (define-key map "?" 'describe-mode)
  1082.   (define-key map "\177" 'forms-prev-record)
  1083.   ;(define-key map "\C-c" map)
  1084.   ;(define-key map "\e" 'ESC-prefix)
  1085.   ;(define-key map "\C-x" ctl-x-map)
  1086.   ;(define-key map "\C-u" 'universal-argument)
  1087.   ;(define-key map "\C-h" help-map)
  1088.   )
  1089.  
  1090. ;;; Changed functions
  1091.  
  1092. (defun forms--change-commands ()
  1093.   "Localize some commands for Forms mode."
  1094.  
  1095.   ;; scroll-down -> forms-prev-record
  1096.   ;; scroll-up -> forms-next-record
  1097.   (if forms-forms-scroll
  1098.       (progn
  1099.     (substitute-key-definition 'scroll-up 'forms-next-record
  1100.                    (current-local-map)
  1101.                    (current-global-map))
  1102.     (substitute-key-definition 'scroll-down 'forms-prev-record
  1103.                    (current-local-map)
  1104.                    (current-global-map))))
  1105.   ;;
  1106.   ;; beginning-of-buffer -> forms-first-record
  1107.   ;; end-of-buffer -> forms-end-record
  1108.   (if forms-forms-jump
  1109.       (progn
  1110.     (substitute-key-definition 'beginning-of-buffer 'forms-first-record
  1111.                    (current-local-map)
  1112.                    (current-global-map))
  1113.     (substitute-key-definition 'end-of-buffer 'forms-last-record
  1114.                    (current-local-map)
  1115.                    (current-global-map))))
  1116.   ;;
  1117.   ;; save-buffer -> forms--save-buffer
  1118.   (add-hook 'local-write-file-hooks
  1119.         (function
  1120.          (lambda (nil)
  1121.            (forms--checkmod)
  1122.            (save-excursion
  1123.          (set-buffer forms--file-buffer)
  1124.          (save-buffer))
  1125.            t))))
  1126.  
  1127. (defun forms--help ()
  1128.   "Initial help for Forms mode."
  1129.   ;; We should use
  1130.   ;;(message (substitute-command-keys (concat
  1131.   ;;"\\[forms-next-record]:next"
  1132.   ;;"   \\[forms-prev-record]:prev"
  1133.   ;;"   \\[forms-first-record]:first"
  1134.   ;;"   \\[forms-last-record]:last"
  1135.   ;;"   \\[describe-mode]:help"
  1136.   ;;"   \\[forms-exit]:exit")))
  1137.   ;; but it's too slow ....
  1138.   (if forms-read-only
  1139.       (message "SPC:next   DEL:prev   <:first   >:last   ?:help   q:exit")
  1140.     (message "C-c n:next   C-c p:prev   C-c <:first   C-c >:last   C-c ?:help   C-c q:exit")))
  1141.  
  1142. (defun forms--trans (subj arg rep)
  1143.   "Translate in SUBJ all chars ARG into char REP.  ARG and REP should
  1144.  be single-char strings."
  1145.   (let ((i 0)
  1146.     (x (length subj))
  1147.     (re (regexp-quote arg))
  1148.     (k (string-to-char rep)))
  1149.     (while (setq i (string-match re subj i))
  1150.       (aset subj i k)
  1151.       (setq i (1+ i)))))
  1152.  
  1153. (defun forms--exit (query &optional save)
  1154.   "Internal exit from forms mode function."
  1155.  
  1156.   (let ((buf (buffer-name forms--file-buffer)))
  1157.     (forms--checkmod)
  1158.     (if (and save
  1159.          (buffer-modified-p forms--file-buffer))
  1160.     (save-excursion
  1161.       (set-buffer forms--file-buffer)
  1162.       (save-buffer)))
  1163.     (save-excursion
  1164.       (set-buffer forms--file-buffer)
  1165.       (delete-auto-save-file-if-necessary)
  1166.       (kill-buffer (current-buffer)))
  1167.     (if (get-buffer buf)    ; not killed???
  1168.       (if save
  1169.       (progn
  1170.         (beep)
  1171.         (message "Problem saving buffers?")))
  1172.       (delete-auto-save-file-if-necessary)
  1173.       (kill-buffer (current-buffer)))))
  1174.  
  1175. (defun forms--get-record ()
  1176.   "Fetch the current record from the file buffer."
  1177.  
  1178.   ;; This function is executed in the context of the `forms--file-buffer'.
  1179.  
  1180.   (or (bolp)
  1181.       (beginning-of-line nil))
  1182.   (let ((here (point)))
  1183.     (prog2
  1184.      (end-of-line)
  1185.      (buffer-substring here (point))
  1186.      (goto-char here))))
  1187.  
  1188. (defun forms--show-record (the-record)
  1189.   "Format THE-RECORD and display it in the current buffer."
  1190.  
  1191.   ;; Split the-record.
  1192.   (let (the-result
  1193.     (start-pos 0)
  1194.     found-pos
  1195.     (field-sep-length (length forms-field-sep)))
  1196.     (if forms-multi-line
  1197.     (forms--trans the-record forms-multi-line "\n"))
  1198.     ;; Add an extra separator (makes splitting easy).
  1199.     (setq the-record (concat the-record forms-field-sep))
  1200.     (while (setq found-pos (string-match forms-field-sep the-record start-pos))
  1201.       (let ((ent (substring the-record start-pos found-pos)))
  1202.     (setq the-result
  1203.           (append the-result (list ent)))
  1204.     (setq start-pos (+ field-sep-length found-pos))))
  1205.     (setq forms--the-record-list the-result))
  1206.  
  1207.   (setq buffer-read-only nil)
  1208.   (if forms-use-text-properties
  1209.       (let ((inhibit-read-only t))
  1210.     (setplist 'forms--electric nil)
  1211.     (set-text-properties (point-min) (point-max) nil)))
  1212.   (erase-buffer)
  1213.  
  1214.   ;; Verify the number of fields, extend forms--the-record-list if needed.
  1215.   (if (= (length forms--the-record-list) forms-number-of-fields)
  1216.       nil
  1217.     (beep)
  1218.     (message "Record has %d fields instead of %d."
  1219.          (length forms--the-record-list) forms-number-of-fields)
  1220.     (if (< (length forms--the-record-list) forms-number-of-fields)
  1221.     (setq forms--the-record-list 
  1222.           (append forms--the-record-list
  1223.               (make-list 
  1224.                (- forms-number-of-fields 
  1225.               (length forms--the-record-list))
  1226.                "")))))
  1227.  
  1228.   ;; Call the formatter function.
  1229.   (setq forms-fields (append (list nil) forms--the-record-list nil))
  1230.   (funcall forms--format forms--the-record-list)
  1231.  
  1232.   ;; Prepare.
  1233.   (goto-char (point-min))
  1234.   (set-buffer-modified-p nil)
  1235.   (setq buffer-read-only forms-read-only)
  1236.   (setq mode-line-process
  1237.     (concat " " forms--current-record "/" forms--total-records)))
  1238.  
  1239. (defun forms--parse-form ()
  1240.   "Parse contents of form into list of strings."
  1241.   ;; The contents of the form are parsed, and a new list of strings
  1242.   ;; is constructed.
  1243.   ;; A vector with the strings from the original record is 
  1244.   ;; constructed, which is updated with the new contents.  Therefore
  1245.   ;; fields which were not in the form are not modified.
  1246.   ;; Finally, the vector is transformed into a list for further processing.
  1247.  
  1248.   (let (forms--recordv)
  1249.  
  1250.     ;; Build the vector.
  1251.     (setq forms--recordv (vconcat forms--the-record-list))
  1252.  
  1253.     ;; Parse the form and update the vector.
  1254.     (let ((forms--dynamic-text forms--dynamic-text))
  1255.       (funcall forms--parser))
  1256.  
  1257.     (if forms--modified-record-filter
  1258.     ;; As a service to the user, we add a zeroth element so she
  1259.     ;; can use the same indices as in the forms definition.
  1260.     (let ((the-fields (vconcat [nil] forms--recordv)))
  1261.       (setq the-fields (funcall forms--modified-record-filter the-fields))
  1262.       (cdr (append the-fields nil)))
  1263.  
  1264.       ;; Transform to a list and return.
  1265.       (append forms--recordv nil))))
  1266.  
  1267. (defun forms--update ()
  1268.   "Update current record with contents of form.
  1269. As a side effect: sets `forms--the-record-list'."
  1270.  
  1271.   (if forms-read-only
  1272.       (progn
  1273.     (message "Read-only buffer!")
  1274.     (beep))
  1275.  
  1276.     (let (the-record)
  1277.       ;; Build new record.
  1278.       (setq forms--the-record-list (forms--parse-form))
  1279.       (setq the-record
  1280.         (mapconcat 'identity forms--the-record-list forms-field-sep))
  1281.  
  1282.       ;; Handle multi-line fields, if allowed.
  1283.       (if forms-multi-line
  1284.       (forms--trans the-record "\n" forms-multi-line))
  1285.  
  1286.       ;; A final sanity check before updating.
  1287.       (if (string-match "\n" the-record)
  1288.       (progn
  1289.         (message "Multi-line fields in this record - update refused!")
  1290.         (beep))
  1291.  
  1292.     (save-excursion
  1293.       (set-buffer forms--file-buffer)
  1294.       ;; Insert something before kill-line is called.  See kill-line
  1295.       ;; doc.  Bugfix provided by Ignatios Souvatzis.
  1296.       (insert "*")
  1297.       (beginning-of-line)
  1298.       (kill-line nil)
  1299.       (insert the-record)
  1300.       (beginning-of-line))))))
  1301.  
  1302. (defun forms--checkmod ()
  1303.   "Check if this form has been modified, and call forms--update if so."
  1304.   (if (buffer-modified-p nil)
  1305.       (let ((here (point)))
  1306.     (forms--update)
  1307.     (set-buffer-modified-p nil)
  1308.     (goto-char here))))
  1309.  
  1310. ;;; Start and exit
  1311.  
  1312. ;;;###autoload
  1313. (defun forms-find-file (fn)
  1314.   "Visit a file in Forms mode."
  1315.   (interactive "fForms file: ")
  1316.   (find-file-read-only fn)
  1317.   (or forms--mode-setup (forms-mode t)))
  1318.  
  1319. ;;;###autoload
  1320. (defun forms-find-file-other-window (fn)
  1321.   "Visit a file in Forms mode in other window."
  1322.   (interactive "fFbrowse file in other window: ")
  1323.   (find-file-other-window fn)
  1324.   (eval-current-buffer)
  1325.   (or forms--mode-setup (forms-mode t)))
  1326.  
  1327. (defun forms-exit (query)
  1328.   "Normal exit from Forms mode.  Modified buffers are saved."
  1329.   (interactive "P")
  1330.   (forms--exit query t))
  1331.  
  1332. (defun forms-exit-no-save (query)
  1333.   "Exit from Forms mode without saving buffers."
  1334.   (interactive "P")
  1335.   (forms--exit query nil))
  1336.  
  1337. ;;; Navigating commands
  1338.  
  1339. (defun forms-next-record (arg)
  1340.   "Advance to the ARGth following record."
  1341.   (interactive "P")
  1342.   (forms-jump-record (+ forms--current-record (prefix-numeric-value arg)) t))
  1343.  
  1344. (defun forms-prev-record (arg)
  1345.   "Advance to the ARGth previous record."
  1346.   (interactive "P")
  1347.   (forms-jump-record (- forms--current-record (prefix-numeric-value arg)) t))
  1348.  
  1349. (defun forms-jump-record (arg &optional relative)
  1350.   "Jump to a random record."
  1351.   (interactive "NRecord number: ")
  1352.  
  1353.   ;; Verify that the record number is within range.
  1354.   (if (or (> arg forms--total-records)
  1355.       (<= arg 0))
  1356.     (progn
  1357.       (beep)
  1358.       ;; Don't give the message if just paging.
  1359.       (if (not relative)
  1360.       (message "Record number %d out of range 1..%d"
  1361.            arg forms--total-records))
  1362.       )
  1363.  
  1364.     ;; Flush.
  1365.     (forms--checkmod)
  1366.  
  1367.     ;; Calculate displacement.
  1368.     (let ((disp (- arg forms--current-record))
  1369.       (cur forms--current-record))
  1370.  
  1371.       ;; `forms--show-record' needs it now.
  1372.       (setq forms--current-record arg)
  1373.  
  1374.       ;; Get the record and show it.
  1375.       (forms--show-record
  1376.        (save-excursion
  1377.      (set-buffer forms--file-buffer)
  1378.      (beginning-of-line)
  1379.  
  1380.      ;; Move, and adjust the amount if needed (shouldn't happen).
  1381.      (if relative
  1382.          (if (zerop disp)
  1383.          nil
  1384.            (setq cur (+ cur disp (- (forward-line disp)))))
  1385.        (setq cur (+ cur disp (- (goto-line arg)))))
  1386.  
  1387.      (forms--get-record)))
  1388.  
  1389.       ;; This shouldn't happen.
  1390.       (if (/= forms--current-record cur)
  1391.       (progn
  1392.         (setq forms--current-record cur)
  1393.         (beep)
  1394.         (message "Stuck at record %d." cur))))))
  1395.  
  1396. (defun forms-first-record ()
  1397.   "Jump to first record."
  1398.   (interactive)
  1399.   (forms-jump-record 1))
  1400.  
  1401. (defun forms-last-record ()
  1402.   "Jump to last record.
  1403. As a side effect: re-calculates the number of records in the data file."
  1404.   (interactive)
  1405.   (let
  1406.       ((numrec 
  1407.     (save-excursion
  1408.       (set-buffer forms--file-buffer)
  1409.       (count-lines (point-min) (point-max)))))
  1410.     (if (= numrec forms--total-records)
  1411.     nil
  1412.       (beep)
  1413.       (setq forms--total-records numrec)
  1414.       (message "Number of records reset to %d." forms--total-records)))
  1415.   (forms-jump-record forms--total-records))
  1416.  
  1417. ;;; Other commands
  1418.  
  1419. (defun forms-view-mode ()
  1420.   "Visit buffer read-only."
  1421.   (interactive)
  1422.   (if forms-read-only
  1423.       nil
  1424.     (forms--checkmod)            ; sync
  1425.     (setq forms-read-only t)
  1426.     (forms-mode)))
  1427.  
  1428. (defun forms-edit-mode ()
  1429.   "Make form suitable for editing, if possible."
  1430.   (interactive)
  1431.   (let ((ro forms-read-only))
  1432.     (if (save-excursion
  1433.       (set-buffer forms--file-buffer)
  1434.       buffer-read-only)
  1435.     (progn
  1436.       (setq forms-read-only t)
  1437.       (message "No write access to \"%s\"" forms-file)
  1438.       (beep))
  1439.       (setq forms-read-only nil))
  1440.     (if (equal ro forms-read-only)
  1441.     nil
  1442.       (forms-mode))))
  1443.  
  1444. ;; Sample:
  1445. ;; (defun my-new-record-filter (the-fields)
  1446. ;;   ;; numbers are relative to 1
  1447. ;;   (aset the-fields 4 (current-time-string))
  1448. ;;   (aset the-fields 6 (user-login-name))
  1449. ;;   the-list)
  1450. ;; (setq forms-new-record-filter 'my-new-record-filter)
  1451.  
  1452. (defun forms-insert-record (arg)
  1453.   "Create a new record before the current one.
  1454. With ARG: store the record after the current one.
  1455. If a function `forms-new-record-filter' is defined, or 
  1456. `forms-new-record-filter' contains the name of a function, 
  1457. it is called to fill (some of) the fields with default values."
  1458.  ; The above doc is not true, but for documentary purposes only
  1459.  
  1460.   (interactive "P")
  1461.  
  1462.   (let ((ln (if arg (1+ forms--current-record) forms--current-record))
  1463.         the-list the-record)
  1464.  
  1465.     (forms--checkmod)
  1466.     (if forms--new-record-filter
  1467.     ;; As a service to the user, we add a zeroth element so she
  1468.     ;; can use the same indices as in the forms definition.
  1469.     (let ((the-fields (make-vector (1+ forms-number-of-fields) "")))
  1470.       (setq the-fields (funcall forms--new-record-filter the-fields))
  1471.       (setq the-list (cdr (append the-fields nil))))
  1472.       (setq the-list (make-list forms-number-of-fields "")))
  1473.  
  1474.     (setq the-record
  1475.       (mapconcat
  1476.       'identity
  1477.       the-list
  1478.       forms-field-sep))
  1479.  
  1480.     (save-excursion
  1481.       (set-buffer forms--file-buffer)
  1482.       (goto-line ln)
  1483.       (open-line 1)
  1484.       (insert the-record)
  1485.       (beginning-of-line))
  1486.     
  1487.     (setq forms--current-record ln))
  1488.  
  1489.   (setq forms--total-records (1+ forms--total-records))
  1490.   (forms-jump-record forms--current-record))
  1491.  
  1492. (defun forms-delete-record (arg)
  1493.   "Deletes a record.  With a prefix argument: don't ask."
  1494.   (interactive "P")
  1495.   (forms--checkmod)
  1496.   (if (or arg
  1497.       (y-or-n-p "Really delete this record? "))
  1498.       (let ((ln forms--current-record))
  1499.     (save-excursion
  1500.       (set-buffer forms--file-buffer)
  1501.       (goto-line ln)
  1502.       (kill-line 1))
  1503.     (setq forms--total-records (1- forms--total-records))
  1504.     (if (> forms--current-record forms--total-records)
  1505.         (setq forms--current-record forms--total-records))
  1506.     (forms-jump-record forms--current-record)))
  1507.   (message ""))
  1508.  
  1509. (defun forms-search (regexp)
  1510.   "Search REGEXP in file buffer."
  1511.   (interactive 
  1512.    (list (read-string (concat "Search for" 
  1513.                   (if forms--search-regexp
  1514.                    (concat " ("
  1515.                        forms--search-regexp
  1516.                        ")"))
  1517.                   ": "))))
  1518.   (if (equal "" regexp)
  1519.       (setq regexp forms--search-regexp))
  1520.   (forms--checkmod)
  1521.  
  1522.   (let (the-line the-record here
  1523.          (fld-sep forms-field-sep))
  1524.     (if (save-excursion
  1525.       (set-buffer forms--file-buffer)
  1526.       (setq here (point))
  1527.       (end-of-line)
  1528.       (if (null (re-search-forward regexp nil t))
  1529.           (progn
  1530.         (goto-char here)
  1531.         (message (concat "\"" regexp "\" not found."))
  1532.         nil)
  1533.         (setq the-record (forms--get-record))
  1534.         (setq the-line (1+ (count-lines (point-min) (point))))))
  1535.     (progn
  1536.       (setq forms--current-record the-line)
  1537.       (forms--show-record the-record)
  1538.       (re-search-forward regexp nil t))))
  1539.   (setq forms--search-regexp regexp))
  1540.  
  1541. (defun forms-revert-buffer (&optional arg noconfirm)
  1542.   "Reverts current form to un-modified."
  1543.   (interactive "P")
  1544.   (if (or noconfirm
  1545.       (yes-or-no-p "Revert form to unmodified? "))
  1546.       (progn
  1547.     (set-buffer-modified-p nil)
  1548.     (forms-jump-record forms--current-record))))
  1549.  
  1550. (defun forms-next-field (arg)
  1551.   "Jump to ARG-th next field."
  1552.   (interactive "p")
  1553.  
  1554.   (let ((i 0)
  1555.     (here (point))
  1556.     there
  1557.     (cnt 0))
  1558.  
  1559.     (if (zerop arg)
  1560.     (setq cnt 1)
  1561.       (setq cnt (+ cnt arg)))
  1562.  
  1563.     (if (catch 'done
  1564.       (while (< i (length forms--markers))
  1565.         (if (or (null (setq there (aref forms--markers i)))
  1566.             (<= there here))
  1567.         nil
  1568.           (if (<= (setq cnt (1- cnt)) 0)
  1569.           (progn
  1570.             (goto-char there)
  1571.             (throw 'done t))))
  1572.         (setq i (1+ i))))
  1573.     nil
  1574.       (goto-char (aref forms--markers 0)))))
  1575.  
  1576. ;;;
  1577. ;;; Special service
  1578. ;;;
  1579. (defun forms-enumerate (the-fields)
  1580.   "Take a quoted list of symbols, and set their values to sequential numbers.
  1581. The first symbol gets number 1, the second 2 and so on.
  1582. It returns the higest number.
  1583.  
  1584. Usage: (setq forms-number-of-fields
  1585.              (forms-enumerate
  1586.               '(field1 field2 field2 ...)))"
  1587.  
  1588.   (let ((the-index 0))
  1589.     (while the-fields
  1590.       (setq the-index (1+ the-index))
  1591.       (let ((el (car-safe the-fields)))
  1592.     (setq the-fields (cdr-safe the-fields))
  1593.     (set el the-index)))
  1594.     the-index))
  1595.  
  1596. ;;; Debugging
  1597.  
  1598. (defvar forms--debug nil
  1599.   "*Enables forms-mode debugging if not nil.")
  1600.  
  1601. (defun forms--debug (&rest args)
  1602.   "Internal debugging routine."
  1603.   (if forms--debug
  1604.       (let ((ret nil))
  1605.     (while args
  1606.       (let ((el (car-safe args)))
  1607.         (setq args (cdr-safe args))
  1608.         (if (stringp el)
  1609.         (setq ret (concat ret el))
  1610.           (setq ret (concat ret (prin1-to-string el) " = "))
  1611.           (if (boundp el)
  1612.           (let ((vel (eval el)))
  1613.             (setq ret (concat ret (prin1-to-string vel) "\n")))
  1614.         (setq ret (concat ret "<unbound>" "\n")))
  1615.           (if (fboundp el)
  1616.           (setq ret (concat ret (prin1-to-string (symbol-function el)) 
  1617.                     "\n"))))))
  1618.     (save-excursion
  1619.       (set-buffer (get-buffer-create "*forms-mode debug*"))
  1620.       (if (zerop (buffer-size))
  1621.           (emacs-lisp-mode))
  1622.       (goto-char (point-max))
  1623.       (insert ret)))))
  1624.  
  1625. ;;; forms.el ends here.
  1626.